/*
 *  Copyright 2004 Blandware (http://www.blandware.com)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.blandware.atleap.webapp.taglib.core.grid;

import com.blandware.atleap.webapp.taglib.core.grid.util.SetFilter;
import com.blandware.atleap.webapp.util.core.RequestUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts.taglib.TagUtils;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.PageContext;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Collection;
import java.util.Map;


/**
 * <p>Tag for marking column as filterable (Type: set). This will generate a
 * link for displaying set filter window.
 * <br />
 * This tag is only valid when nested within <em>grid</em> tag.
 * </p>
 * <p>
 * Allowed attributes are:
 * <ul>
 * <li>
 * <b>beanId</b> - identifier of Spring bean to use to get list of available
 * elements for set filter
 * </li>
 * <li>
 * <b>method</b> - method of specified bean to call to get list of available
 * elements for set filter. This method must return instance of
 * <code>java.util.Collection</code> and must have no arguments. All elements in
 * returned list will be converted to Strings while iterating them on the page.
 * </li>
 * <li>
 * <b>collection</b> - collection of available elements if beanId is not
 * specified. Only instances of <code>java.util.Collection</code> and
 * <code>java.lang.Object[]</code> are supported.
 * </li>
 * <li>
 * <b>label</b> - property of each bean in collection to get label to display.
 * If not specified, String representation of bean will be used as label.
 * </li>
 * <li>
 * <b>value</b> - property of each bean in collection to get the value to send
 * to action. If not specified, String representation of bean will be used as
 * value.
 * </li>
 * </ul>
 * </p>
 * <p>
 * Some attributes are inherited from {@link BaseFilterTag}.
 * </p>
 * <p>
 * This tag must either be contained in <em>column</em> tag or have
 * <b>fieldName</b> specified.
 * </p>
 * <p>
 * Either <b>collection</b> or both <b>beanId</b> and <b>method</b> must be specified.
 * If <b>beanId</b> and <b>method</b> are specified, collection, over which
 * iteration will be done, will be obtained invoking <b>method</b> on bean,
 * which ID is <b>beanId</b>. Else, <b>collection</b> will be used as such
 * collection.
 * </p>
 * <p>
 * If <b>label</b> is specified, then <b>value</b> must be specified too.
 * </p>
 * <p><a href="SetFilterTag.java.html"><i>View Source</i></a></p>
 *
 * @author Sergey Zubtsovskiy <a href="mailto:sergey.zubtsovskiy@blandware.com">&lt;sergey.zubtsovskiy@blandware.com&gt;</a>
 * @version $Revision: 1.13 $ $Date: 2006/08/03 10:08:21 $
 * @jsp.tag name="setFilter"
 * body-content="scriptless"
 * @see com.blandware.atleap.webapp.taglib.core.grid.GridTag
 */
public class SetFilterTag extends BaseFilterTag {

	protected transient final Log log = LogFactory.getLog(SetFilterTag.class);

	/**
	 * Identifier of Spring bean to use to get list of available elements for set filter.
	 */
	protected String beanId;

	/**
	 * Method of specified bean to call to get list of available elements for set filter.
	 * This method must return instance of <code>java.util.List</code> and
	 * must have no arguments. All elements in returned list will be converted to Strings
	 * while iterating them on the page.
	 *
	 * @see java.util.List
	 */
	protected String method;

	/**
	 * Collection of available elements if beanId not specified.
	 * Only instances of <code>java.util.Collection</code> and
     * <code>java.lang.Object[]</code> are supported.
	 */
	protected Object collection;

	/**
	 * Property of each bean in collection to get label to display
	 */
	protected String label;

	/**
	 * Property of each bean in collection to get the value to send to action
	 */
	protected String value;

	/**
     * Returns ID of bean that will supply the universe set
     *
	 * @return bean ID
     * @see #beanId
	 * @jsp.attribute required="false"
	 * rtexprvalue="true"
	 * type="java.lang.String"
	 * description="Identifier of Spring bean to use to get list for set filter"
	 */
	public String getBeanId() {
		return beanId;
	}

    /**
     * Sets ID of bean that will supply the universe set
     *
     * @param beanId bean ID to set
     * @see #beanId
     */
	public void setBeanId(String beanId) {
		this.beanId = beanId;
	}

	/**
     * Returns name of method of bean that will be used to obtain the universe set
     *
	 * @return method name
     * @see #method
	 * @jsp.attribute required="false"
	 * rtexprvalue="true"
	 * type="java.lang.String"
	 * description="Method of specified bean to call to get list of available elements for set filter"
	 */
	public String getMethod() {
		return method;
	}

    /**
     * Sets name of method of bean that will be used to obtain the universe set
     *
     * @param method method name to set
     * @see #method
     */
	public void setMethod(String method) {
		this.method = method;
	}

	/**
     * Returns collection of elements (used as universe set if bean ID is not specified)
     *
	 * @return collection
     * @see #collection
	 * @jsp.attribute required="false"
	 * rtexprvalue="true"
	 * type="java.lang.Object"
	 * description="Collection of available elements to display if beanId not specified"
	 */
	public Object getCollection() {
		return collection;
	}

    /**
     * Sets collection of elements (used as universe set if bean ID is not specified)
     *
     * @param collection collection to set
     * @see #collection
     */
	public void setCollection(Object collection) {
		this.collection = collection;
	}

	/**
     * Returns name of property of each bean that will be used to get label
     *
	 * @return label property name
     * @see #label
	 * @jsp.attribute required="false"
	 * rtexprvalue="true"
	 * type="java.lang.String"
	 * description="Property of each bean in collection to get label to display"
	 */
	public String getLabel() {
		return label;
	}

    /**
     * Sets name of property of each bean that will be used to get label
     *
     * @param label label property name to set
     * @see #label
     */
	public void setLabel(String label) {
		this.label = label;
	}

	/**
     * Returns name of property of each bean that will be used to get value
     *
	 * @return value propery name
     * @see #value
	 * @jsp.attribute required="false"
	 * rtexprvalue="true"
	 * type="java.lang.String"
	 * description="Property of each bean in collection to get the value to send to action"
	 */
	public String getValue() {
		return value;
	}

    /**
     * Sets name of property of each bean that will be used to get value
     *
     * @param value value propery name to set
     * @see #value
     */
	public void setValue(String value) {
		this.value = value;
	}

    /**
     * Processes the tag
     *
     * @throws JspException
     * @throws IOException
     */
	public void doTag() throws JspException, IOException {

		PageContext pageContext = (PageContext) getJspContext();

		GridTag parentGridTag = (GridTag) findAncestorWithClass(this, GridTag.class);
		if ( parentGridTag == null ) {
			JspTagException e = new JspTagException("Parent tag is invalid! This tag is only valid when nested within 'grid' tag");
			throw e;
		}

		// Get field name and field key from parent 'column' tag
		ColumnTag parentColumnTag = (ColumnTag) findAncestorWithClass(this, ColumnTag.class);
		if ( parentColumnTag != null ) {
			if ( fieldName == null || fieldName.length() == 0 ) {
				fieldName = parentColumnTag.getFieldName();
			}

			if ( fieldKey == null || fieldKey.length() == 0 ) {
				fieldKey = parentColumnTag.getFieldKey();
			}
		}

		if ( fieldName == null || "".equals(fieldName) ) {
			// Error: there is no ancestor 'column' tag and no fieldName attribute is specified
			String errorMessage = "There is no ancestor 'column' tag and no fieldName attribute is specified!";
			if ( log.isErrorEnabled() ) {
				log.error(errorMessage);
			}
			JspTagException e = new JspTagException(errorMessage);
			throw e;
		}

		// check beanId, method and collection attributes
		if ( (beanId == null || method == null) && collection == null ) {
			// error - either collection or beanId with method attributes must be specified
			String errorMessage = "Either collection or beanId with method attributes must be specified";
			JspTagException e = new JspTagException(errorMessage);
			throw e;
		}

		if ( label != null && label.length() > 0 && (value == null || value.length() == 0) ) {
			// error - either value or label and value properties must be specified, or both omitted
			String errorMessage = "Either value or label and value properties must be specified, or both omitted";
			JspTagException e = new JspTagException(errorMessage);
			throw e;
		}

		String pageUrl = RequestUtil.getPageUrl(parentGridTag);

		String scriptCall = "showSetFilterWindow(\'" + fieldName + "\', \'" + parentGridTag.getName() +
		        "\', \'" + pageUrl + "\'";

		if ( fieldKey == null ) {
			fieldKey = "";
		}

		scriptCall += ", \'" + fieldKey + "\'";

		if ( beanId == null || method == null ) {
			beanId = "";
			method = "";
			// collection is specified - it is guaranteed above, so check its class
			if ( !(collection instanceof Map) && !(collection instanceof Collection) && !(collection instanceof Object[]) ) {
				String errorMessage = "Only instances of java.util.Collection, java.util.Map and java.lang.Object[] are supported";
				JspTagException e = new JspTagException(errorMessage);
				throw e;
			}

			pageContext.getSession().setAttribute(SetFilter.AVAILABLE_ELEMENTS, collection);
		}

		scriptCall += ", \'" + beanId + "\', \'" + method + "\'";

		if ( label == null ) {
			label = "";
		}

		scriptCall += ", \'" + label + "\'";

		if ( value == null ) {
			value = "";
		}

		scriptCall += ", \'" + value + "\'";

		if ( rowIterators == null ) {
			rowIterators = "";
		}

		scriptCall += ", \'" + rowIterators + "\'";

		scriptCall += ");";

		if ( onclick != null && onclick.length() != 0 ) {
			int k = onclick.indexOf("return false");
			if ( k != -1 ) {
				onclick = onclick.substring(0, k);
			}
		} else {
			onclick = new String();
		}
		onclick += scriptCall + " return false;";
		StringWriter sw = new StringWriter();
		StringBuffer sb = sw.getBuffer();
		sb.append("<a href=\"javascript:void(0)\"").append(prepareAttributes()).append(">");
		getJspBody().invoke(sw);
		sb.append("</a>");
		TagUtils.getInstance().write(pageContext, sw.toString());
	}

}
